iT邦幫忙

2021 iThome 鐵人賽

DAY 2
2
Software Development

猴子都寫得出來的 RISC-V CPU Emulator系列 第 10

RISC-V: 作為第一道指令的聰明選擇

  • 分享至 

  • xImage
  •  

第一道指令先從 Immediat 系列開始吧
指令系列會依照時間安排增減速度,
一方面是因為上一篇留下的技術債要想辦法 Refactor,
另一方面是要留時間提前準備接下來的內容。

根據 Andes RISC-V Conf’21 的資訊,
Tiny ONNC build 出來的執行檔可以在 Bare Metal 環境下執行,
這段時間會對相關的 Softwere Stack 進行研究,
預計會用 ONNC 或者 tiny ONNC 來為這篇系列文收尾。
期待這次的成果,一定是大拇指的拉!

I-type

指令格式如下:

|31     20|19   15|14    12|11   7|6       0|
+-------------------------------------------+
|   imm   |  rs1  | funct3 |  rd  | pocode  |
+-------------------------------------------+

ADDI

ADDI 會把 rs1 + imm 存到 rd 裡面。
運算時遇到 Overflow 不進行任何處理,
直接把最低的 32 bit 放到 rd 內。

|31     20|19   15|14    12|11   7|6       0|
+-------------------------------------------+
|   imm   |  rs1  |  000   |  rd  | 0010011 |
+-------------------------------------------+

NOP

沒錯, opcode 和 funct3 都沒有打錯,跟 ADDI 一模一樣,
從黑洞拿 0,加 0 再塞回去,官方認證的 NOP。
實做一道指令可以當兩道用,讚!

|31     20|19   15|14    12|11   7|6       0|
+-------------------------------------------+
|    0    |   0   |  000   |   0  | 0010011 |
+-------------------------------------------+

實際程式

github 頁面 Tag: ITDay10

這次新增了 EXECUTOR 來管理 CPU 的執行部分,
在 CPU 內只會呼叫 execute 讓 EXECUTOR 執行 decoder 內的指令。

class EXECUTOR_INTERFACE
{
public:
	virtual void execute() = 0;
	void set_instruction_decoder(const std::shared_ptr<INSTRUCTION_DECODER_INTERFACE> &instance)
	{

		instruction_decoder = instance;
	}
	void set_register_file(const std::shared_ptr<REGISTER_INTERFACE> &instance)
	{
		register_file = instance;
	}

protected:
	std::shared_ptr<INSTRUCTION_DECODER_INTERFACE> instruction_decoder;
	std::shared_ptr<REGISTER_INTERFACE> register_file;
};

這次實作了第一道指令:ADDI
同時這道指令也是 NOP,因為 destination 寫到 x0 並不會影響 x0 的值。

void EXECUTOR::execute()
{
	if(instruction_decoder->get_opcode() == INSTRUCTION_DECODER_INTERFACE::OP_IMM &&
	   instruction_decoder->get_func3() == 0b000) {
		auto rs1 = instruction_decoder->get_rs1();
		auto rd = instruction_decoder->get_rd();

		auto value = register_file->get_value_integer(rs1) + instruction_decoder->get_imm(31, 20);
		register_file->set_value_integer(rd, value);
...
	}
}

EXECUTOR 需要 memory、register file 和 instruction decoder 的配合才能工作,
這次先只把 instruction_decoderregister_file 放進去。

class EXECUTOR_INTERFACE
{
...
	std::shared_ptr<INSTRUCTION_DECODER_INTERFACE> instruction_decoder;
	std::shared_ptr<REGISTER_INTERFACE> register_file;
};

前面的文章有提到,記憶體分成 explict 和 implict 存取,
這邊將 explice 存取由 EXECUTOR 處理, implict 存取則由 CPU 統一負責。
Instance 由 CPU 管理,所以在 setter 中做了對應的修改。

void CPU::set_register_file(const std::shared_ptr<REGISTER_INTERFACE> &instance)
{
...
		executor->set_register_file(register_file);
...
}

void CPU::set_instruction_decoder(const std::shared_ptr<INSTRUCTION_DECODER_INTERFACE> &instance)
{
...
		executor->set_instruction_decoder(instruction_decoder);
...
}

void CPU::set_executor(const std::shared_ptr<EXECUTOR_INTERFACE> &instance)
{
...
		executor->set_instruction_decoder(instruction_decoder);
...
		executor->set_register_file(register_file);
}

shared_ptr 和做 interface 的理由和之前一樣,不再贅述。


上一篇
RISC-V: 指令解碼器
下一篇
RISC-V: I-type 位元運算指令
系列文
猴子都寫得出來的 RISC-V CPU Emulator31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言